package pl.edu.agh.neuraleconomy.ui.component;

import java.lang.reflect.InvocationTargetException;
import java.util.Date;

import org.apache.log4j.Logger;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;

import pl.edu.agh.neuraleconomy.common.utils.DateUtils;
import pl.edu.agh.neuraleconomy.core.simulation.SimulationEngine;
import pl.edu.agh.neuraleconomy.core.simulation.SimulationService;
import pl.edu.agh.neuraleconomy.dataimporter.HTMLImporter;
import pl.edu.agh.neuraleconomy.persistence.base.DaoProvider;
import pl.edu.agh.neuraleconomy.ui.Context;
import pl.edu.agh.neuraleconomy.ui.util.UIUtils;

public class MainWindow extends Composite {
	private Logger logger = Logger.getLogger(getClass());
	private Shell shell;

	private ChartPanel chartPanel;
	private SimulationPanel simPanel;

	private MenuItem simulationSaveItem;
	private SimulationService simulationService = new SimulationService();

	public MainWindow(Composite parent, int style) {
		super(parent, style);
		this.shell = (Shell) Context.get(Context.SHELL);
		init();
	}

	private void init() {
		setLayout(new GridLayout(1, false));
		addMenu();
		addRootPanel();
	}

	private void addMenu() {
		Menu menuBar, fileMenu, simulationMenu, helpMenu;
		MenuItem fileMenuHeader, simulationMenuHeader, helpMenuHeader;
		MenuItem fileExitItem, fileImportItem;
		MenuItem simulationNewItem, simulationLoadItem;
		MenuItem helpGetHelpItem;

		menuBar = new Menu(shell, SWT.BAR);
		shell.setMenuBar(menuBar);

		// FILE
		fileMenuHeader = new MenuItem(menuBar, SWT.CASCADE);
		fileMenuHeader.setText("File");

		fileMenu = new Menu(shell, SWT.DROP_DOWN);
		fileMenuHeader.setMenu(fileMenu);

		fileImportItem = new MenuItem(fileMenu, SWT.PUSH);
		fileImportItem.setText("Import data");
		fileImportItem.addSelectionListener(new SelectionAdapter() {

			@Override
			public void widgetSelected(SelectionEvent arg0) {
				importData();
			}

		});

		fileExitItem = new MenuItem(fileMenu, SWT.PUSH);
		fileExitItem.setText("Exit");

		// SIMULATION
		simulationMenuHeader = new MenuItem(menuBar, SWT.CASCADE);
		simulationMenuHeader.setText("Simulation");

		simulationMenu = new Menu(shell, SWT.DROP_DOWN);
		simulationMenuHeader.setMenu(simulationMenu);

		simulationNewItem = new MenuItem(simulationMenu, SWT.PUSH);
		simulationNewItem.setText("New");
		simulationNewItem.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				newSimulation();
			}
		});

		simulationLoadItem = new MenuItem(simulationMenu, SWT.PUSH);
		simulationLoadItem.setText("Load");
		simulationLoadItem.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				loadSimulation();
			}
		});

		simulationSaveItem = new MenuItem(simulationMenu, SWT.PUSH);
		simulationSaveItem.setText("Save");
		simulationSaveItem.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				saveSimulation(true);
			}
		});

		// HELP
		helpMenuHeader = new MenuItem(menuBar, SWT.CASCADE);
		helpMenuHeader.setText("Help");

		helpMenu = new Menu(shell, SWT.DROP_DOWN);
		helpMenuHeader.setMenu(helpMenu);

		helpGetHelpItem = new MenuItem(helpMenu, SWT.PUSH);
		helpGetHelpItem.setText("Get Help");

		refreshMenu();
	}

	private void addRootPanel() {
		TabFolder folder = new TabFolder(this, SWT.NONE);
		folder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
		Context.put(Context.TAB_FOLDER, folder);

		// CHART TAB
		TabItem chartTab = new TabItem(folder, SWT.NONE);
		chartTab.setText("Chart");
		chartPanel = new ChartPanel(folder, SWT.NONE);
		chartTab.setControl(chartPanel);
		Context.put(Context.CHART_PANEL, chartPanel);

		// SIMULATION TAB
		TabItem simTab = new TabItem(folder, SWT.NONE);
		simTab.setText("Simulation");
		simPanel = new SimulationPanel(folder, SWT.NONE);
		simTab.setControl(simPanel);

	}

	private void newSimulation() {
		if (Context.getSimulation() != null) {
			if (!askSaveSimulation()) {
				return;
			}
		}

		NewSimulationDialog dialog = new NewSimulationDialog(shell);
		dialog.create();
		if (dialog.open() == Window.OK) {
			SimulationEngine simulation = new SimulationEngine(dialog.getStartDate(), dialog.getStartMoney(), dialog.getName());
			Context.put(Context.SIMULATION, simulation);
			simPanel.refresh();
		}

		refreshMenu();
		selectSimulationTab();
	}

	private boolean askSaveSimulation() {
		try {
			MessageBox dialog = new MessageBox(shell, SWT.ICON_QUESTION | SWT.YES | SWT.NO | SWT.CANCEL);
			dialog.setMessage("Do you want to save current simulation?");
			int result = dialog.open();
			if (result == SWT.YES) {
				saveSimulation(false);
				return true;
			}
			if (result == SWT.NO) {
				return true;
			}
			return false;
		} catch (Exception e) {
			UIUtils.showError(e.getMessage());
			return false;
		}

	}

	private void saveSimulation(boolean succesMsg) {
		try {
			simulationService.saveSimulation(Context.getSimulation());

			if (!succesMsg) {
				return;
			}

			MessageBox dialog = new MessageBox(shell, SWT.OK);
			dialog.setText("Success");
			dialog.setMessage("Simulation saved successfully");
			dialog.open();
		} catch (Exception e) {
			UIUtils.showError(e.getMessage());
		}
	}

	private void loadSimulation() {
		if (Context.getSimulation() != null) {
			if (!askSaveSimulation()) {
				return;
			}
		}

		LoadSimulationDialog dialog = new LoadSimulationDialog(shell);
		dialog.create();
		if (dialog.open() == Window.OK) {
			SimulationEngine simulation = simulationService.load(dialog.getSimulationId());
			Context.put(Context.SIMULATION, simulation);
			simPanel.refresh();
		}

		refreshMenu();
		selectSimulationTab();
	}

	private void refreshMenu() {
		simulationSaveItem.setEnabled(Context.getSimulation() != null);
	}

	private void selectSimulationTab() {
		Context.getTabFolder().setSelection(1);
	}

	private void importData() {
		ImportDataDialog dialog = new ImportDataDialog(shell);
		dialog.create();

		Date newest = DaoProvider.getExchangeDao().getNewestDate();
		if (newest != null) {
			dialog.setDateFrom(DateUtils.addDays(newest, 1));
		}
		dialog.setDateTo(new Date());

		if (dialog.open() == Window.OK) {
			importData(dialog.getDateFrom(), dialog.getDateTo());
		}
	}

	private void importData(Date from, Date to) {
		try {
			new ProgressMonitorDialog(getShell()).run(true, true, new DataDownloader(from, to));
		} catch (Exception e) {
			logger.error("", e);
			UIUtils.showError("Exception while importing data. See logs for more information");
		}
	}

	private class DataDownloader extends HTMLImporter implements IRunnableWithProgress {
		private Date dateFrom;
		private Date dateTo;
		private IProgressMonitor monitor;

		DataDownloader(Date dateFrom, Date dateTo) {
			this.dateFrom = dateFrom;
			this.dateTo = dateTo;
		}

		public void run(IProgressMonitor m) throws InvocationTargetException, InterruptedException {
			this.monitor = m;
			monitor.beginTask("Importing data", DateUtils.getDaysDifferenceBetweenDates(dateFrom, dateTo));
			this.importData(dateFrom, dateTo);
			monitor.done();
		}

		@Override
		public void importForDate(Date date) {
			monitor.subTask(DateUtils.formatDate(date));
			if (!monitor.isCanceled()) {
				super.importForDate(date);
			}
			monitor.worked(1);
		}

	}

}
